import { Bits } from "../util/Bits";
import { Cls } from "../util/Defined";
import { TypeUtil } from "../util/TypeUtil";
import { ECSCmp } from "./ECSCmp";
import { ECSCmpMgr } from "./ECSCmpMgr";
import { ECSEntity } from "./ECSEntity";
import { ECSFilter, IECSFilter } from "./ECSFilter";
import { ECSScene } from "./ECSScene";

const bits = new Bits();
/**
 * [ECS]过滤器管理
 */
export class ECSFilterMgr {

    /** 相同key的filter */
    protected _filterMap = new Map<string, ECSFilter>();
    /** 关注cmp的filter */
    protected _watchCmpTypeFilterMap = new Map<Cls<ECSCmp>, Set<ECSFilter>>();
    /** 过滤器发生变化 */
    protected _filterDirty = false;

    constructor(
        /** 所在场景 */
        readonly scene: ECSScene
    ) { }

    /**
     * 生成过滤器 相同参数的过滤器只会生成一个
     * @param ifilter 过滤器参数
     */
    genFilter(ifilter?: IECSFilter) {
        let key = this._getKey(ifilter);
        if (key) {
            let filter = this._filterMap.get(key);
            if (filter) {
                ++filter.ref;
            } else {
                this._filterDirty = true;

                //all里只有一个的都当作one
                if (!(ifilter.includeOne?.length > 0) && ifilter.includeAll?.length === 1) {
                    ifilter.includeOne = ifilter.includeAll;
                    delete ifilter.includeAll;
                }

                if (!(ifilter.excludeOne?.length > 0) && ifilter.excludeAll?.length === 1) {
                    ifilter.excludeOne = ifilter.excludeAll;
                    delete ifilter.excludeAll;
                }

                filter = new ECSFilter(key, ifilter);
                filter.watchCmpTypes.forEach(cmpType => {
                    let watches = this._watchCmpTypeFilterMap.get(cmpType);
                    if (!watches) {
                        watches = new Set();
                        this._watchCmpTypeFilterMap.set(cmpType, watches);
                    }
                    watches.add(filter);
                });
                this._filterMap.set(key, filter);
            }
            return filter;
        }
        return null;
    }

    /**
     * 移除过滤器
     * @param filter 过滤器
     */
    removeFilter(filter: ECSFilter) {
        if (this._filterMap.has(filter.key) && --filter.ref === 0) {
            this._filterMap.delete(filter.key);
            filter.watchCmpTypes.forEach(cmpType => this._watchCmpTypeFilterMap.get(cmpType).delete(filter));
            filter.destroy();
        }
    }

    /**
     * 当实体被添加时通知过滤器
     * @param entity 实体
     */
    onEntityAdd(entity: ECSEntity) {
        this._filterDirty = true;
        this._filterMap.forEach(filter => filter.onEntityChange(entity));
    }

    /**
     * 当实体被移除时通知过滤器
     * @param entity 实体
     */
    onEntityRemove(entity: ECSEntity) {
        this._filterDirty = true;
        this._filterMap.forEach(filter => filter.onEntityChange(entity));
    }

    /**
     * 当组件被添加时，通知关注该组件类型的filter更新
     * @param cmp 组件
     */
    onCmpAdd(cmp: ECSCmp) {
        const filters = this._watchCmpTypeFilterMap.get(TypeUtil.getCls(cmp));
        if (filters) {
            this._filterDirty = true;
            filters.forEach(filter => {
                filter.onCmpAdd(cmp);
            });
        }
    }

    /**
     * 当组件被移除时，通知关注该组件类型的filter更新
     * @param cmp 组件
     */
    onCmpRemove(cmp: ECSCmp) {
        const filters = this._watchCmpTypeFilterMap.get(TypeUtil.getCls(cmp));
        if (filters) {
            this._filterDirty = true;
            filters.forEach(filter => {
                filter.onCmpRemove(cmp);
            });
        }
    }

    /**
     * 当有组件变化时在同一时间里统一更新
     */
    update() {
        if (this._filterDirty) {
            this._filterDirty = false;
            this._filterMap.forEach(filter => filter.update());
        }
    }

    /**
     * 场景停止时销毁
     */
    destroy() {
        this._filterMap.forEach(filter => filter.destroy());
        this._filterMap.clear();
        this._watchCmpTypeFilterMap.clear();
    }

    protected _getKey(ifilter?: IECSFilter) {
        let key = "";
        if (ifilter && (ifilter.includeOne || ifilter.includeAll || ifilter.excludeOne || ifilter.excludeAll)) {
            if (ifilter.includeOne) {
                bits.reset();
                ifilter.includeOne.forEach(cmp => bits.setBit(ECSCmpMgr.getCmpIdx(cmp), true));
                key += bits.toString(16);
            }
            key += "|";
            if (ifilter.includeAll) {
                bits.reset();
                ifilter.includeAll.forEach(cmp => bits.setBit(ECSCmpMgr.getCmpIdx(cmp), true));
                key += bits.toString(16);
            }
            key += "|";
            if (ifilter.excludeOne) {
                bits.reset();
                ifilter.excludeOne.forEach(cmp => bits.setBit(ECSCmpMgr.getCmpIdx(cmp), true));
                key += bits.toString(16);
            }
            key += "|";
            if (ifilter.excludeAll) {
                bits.reset();
                ifilter.excludeAll.forEach(cmp => bits.setBit(ECSCmpMgr.getCmpIdx(cmp), true));
                key += bits.toString(16);
            }
            bits.reset();
        }
        return key;
    }

}